package com.shockweb.service.ioc.impl.spring;


import java.lang.reflect.Field;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScopeMetadata;
import org.springframework.context.annotation.ScopeMetadataResolver;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;

import com.shockweb.common.log.LogManager;
import com.shockweb.service.data.ServiceStatus;

/**
 * 类注解扫描器
 * @see ClassPathBeanDefinitionScanner
 * 
 * @author 彭明华
 * 2018年1月26日 创建
 */
public final class Scanner extends ClassPathBeanDefinitionScanner {
	/**
	 * 构造方法
	 * @param registry
	 */
	public Scanner(BeanDefinitionRegistry registry) {
		super(registry);
	}
	
	/**
	 * 注册过滤器
	 */
	@Override
	public void registerDefaultFilters() {
		this.addIncludeFilter(new AnnotationTypeFilter(ShockWebService.class));
	}

	
	/**
	 * 获取ClassPathBeanDefinitionScanner的私有属性scopeMetadataResolver
	 * @return
	 */
	private ScopeMetadataResolver getScopeMetadataResolver(){
		try{
			Class<?> classType = ClassPathBeanDefinitionScanner.class;
			Field field = classType.getDeclaredField("scopeMetadataResolver");
			field.setAccessible(true);
			return (ScopeMetadataResolver)field.get(this);
		}catch(Exception ex){
			LogManager.errorLog(this.getClass(), ex);
		}
		return null;
	}
	
	
	/**
	 * 获取ClassPathBeanDefinitionScanner的私有属性beanNameGenerator
	 * @return
	 */
	private BeanNameGenerator getBeanNameGenerator(){
		try{
			Class<?> classType = ClassPathBeanDefinitionScanner.class;
			Field field = classType.getDeclaredField("beanNameGenerator");
			field.setAccessible(true);
			return (BeanNameGenerator)field.get(this);
		}catch(Exception ex){
			LogManager.errorLog(this.getClass(), ex);
		}
		return null;
	}

	
	/**
	 * 扫描方法
	 * @see ClassPathBeanDefinitionScanner#doScan(String... basePackages)
	 */
	@Override
	public Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
		for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = getScopeMetadataResolver().resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = getBeanNameGenerator().generateBeanName(candidate, getRegistry());
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				ServiceStatus.getServices().add(beanName);
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder = applyScopedProxyMode(scopeMetadata, definitionHolder, getRegistry());
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, getRegistry());
				}
			}
		}
		return beanDefinitions;
	}

	/**
	 * @see AnnotationConfigUtils#applyScopedProxyMode(metadata, definition, registry)
	 * @param metadata
	 * @param definition
	 * @param registry
	 * @return
	 */
	private static BeanDefinitionHolder applyScopedProxyMode(
			ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

		ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
		if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
			return definition;
		}
		boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
		return ScopedProxyUtils.createScopedProxy(definition, registry, proxyTargetClass);
	}

	

}
